﻿using DotNetCommon.Extensions;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace DotNetCommon;

/// <summary>
/// 深度克隆帮助类，参照: <seealso cref="DeepCloneHelper.DeepColne{T}(T, bool)"/>
/// </summary>
public class DeepCloneHelper
{
    static DeepCloneHelper()
    {
        DeepCloneHelper.RegisterCloneHander(typeof(Hashtable), (obj, cache) =>
        {
            if (cache.ContainsKey(obj)) return cache.Get(obj);
            var src = obj as Hashtable;
            var compareProp = typeof(Hashtable).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(i => i.Name == "EqualityComparer");
            var res = new Hashtable(src.Count, equalityComparer: compareProp.GetValue(src) as IEqualityComparer);
            cache.Set(obj, res);
            foreach (var item in src.Keys)
            {
                res.Add(item.DeepClone(), src[item].DeepClone());
            }
            return res;
        });
    }

    /// <summary>
    /// 克隆时用的缓存字典,内部封装了 ConcurrentDictionary&lt;object, object>，由 containsRepeatReference 决定是否使用内部的字典
    /// </summary>
    public class CacheDictionary
    {
        private readonly ConcurrentDictionary<object, object> dic;
        private readonly bool containsRepeatReference = true;
        internal CacheDictionary(bool containsRepeatReference)
        {
            this.containsRepeatReference = containsRepeatReference;
            if (containsRepeatReference) dic = new ConcurrentDictionary<object, object>(1, 16);
        }
        /// <summary>
        /// 是否已包含key
        /// </summary>
        public bool ContainsKey(object key)
        {
            if (!containsRepeatReference) return false;
            return dic.ContainsKey(key);
        }
        /// <summary>
        /// 是否已包含key
        /// </summary>
        public bool TryGetValue(object key, out object value)
        {
            value = null;
            if (!containsRepeatReference) return false;
            if (dic.TryGetValue(key, out var value2))
            {
                value = value2;
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取
        /// </summary>
        public object Get(object obj)
        {
            if (!containsRepeatReference) return null;
            return dic.ContainsKey(obj) ? dic[obj] : null;
        }

        /// <summary>
        /// 更新
        /// </summary>
        public void Set(object key, object val)
        {
            if (!containsRepeatReference) return;
            dic.TryAdd(key, val);
        }
    }
    class Wrapper
    {
        public Func<object, CacheDictionary, object> Method { set; get; }
        public object Copy(object obj, CacheDictionary dic) => Method.Invoke(obj, dic);
    }

    private static readonly Type[] simpleTypes =
    [
        typeof(DateTime),typeof(Guid),typeof(TimeSpan), typeof(DateTimeOffset),
        typeof(DBNull),/*DBNull的TypeCode是Object*/
        typeof(Vector2),typeof(Vector3),typeof(Vector4),typeof(Matrix3x2),typeof(Matrix4x4),typeof(Plane),typeof(Quaternion),
        typeof(DateOnly),  typeof(TimeOnly)
    ];
    private static bool JudgeSimple(Type type)
    {
        var typecode = type.GetTypeCode();
        if (typecode == TypeCode.Object)
        {
            if (type.IsNullable())
            {
                type = type.GenericTypeArguments.FirstOrDefault();
                typecode = type.GetTypeCode();
            }
        }
        var flag = false;
        switch (typecode)
        {
            case TypeCode.Empty:
            case TypeCode.DBNull:
            case TypeCode.Boolean:
            case TypeCode.Char:
            case TypeCode.SByte:
            case TypeCode.Byte:
            case TypeCode.Int16:
            case TypeCode.UInt16:
            case TypeCode.Int32:
            case TypeCode.UInt32:
            case TypeCode.Int64:
            case TypeCode.UInt64:
            case TypeCode.Single:
            case TypeCode.Double:
            case TypeCode.Decimal:
            case TypeCode.DateTime:
            case TypeCode.String:
                return true;
            case TypeCode.Object:
            default:
                break;
        }
        if (simpleTypes.Contains(type))
        {
            flag = true;
        }
        return flag;
    }

    private static readonly ConcurrentDictionary<Type, Wrapper> _cache = new();

    /// <summary>
    /// 深度克隆对象实例，支持 <c>简单类poco</c>、<c>数组</c>、<c>List</c>、<c>dicitionary</c>、<c>ValueTuple&lt;></c>、<c>匿名类型</c> 等, 示例代码:
    /// <code>
    /// var list=new List&lt;Person>()
    /// {
    ///     new Person{ Id = 1, Name = "小明" },
    ///     new Person{ Id = 2, Name = "小刚" }
    /// }
    /// var newList = list.DeepClone(false);//因为数据格式简单,没有冗余、循环的引用,传入 false 将克隆缓存关掉以提升性能
    /// </code>
    /// 当实例内部有冗余、循环的引用时:
    /// <code>
    /// var root = new Node { Id = 1, Children = new List&lt;Node>() };
    /// var current = new Node { Id = 2,Parent=root };
    /// root.Children.Add(current);
    /// var newRoot = root.DeepClone(true);//因为数据之间有冗余、循环的引用, 传入 true 打开克隆缓存，引用关系将被一起克隆
    /// </code>
    /// </summary>
    /// <remarks>
    /// 注意：本方法优先使用 <see cref="ICloneable"/> 中的 <c>Clone</c> 方法
    /// </remarks>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    /// <param name="containsRepeatReference">是否考虑重复的引用,当要克隆的对象内部没有冗余的、循环的引用时,将此设为 <c>false</c> 可能提升一半性能</param>
    /// <returns></returns>
    public static T DeepColne<T>(T obj, bool containsRepeatReference = true)
    {
        var type = obj?.GetType();
        if (type == null) return default;
        if (_cache.TryGetValue(type, out var wrapper))
        {
            return (T)wrapper.Method(obj, new CacheDictionary(containsRepeatReference));
        }
        var tmpDic = new ConcurrentDictionary<Type, Func<object>>();
        var wrapperNew = _cache.GetOrAdd(type, type =>
        {
            var tmpCache = new Dictionary<Type, Wrapper>();
            var func = GetCloneMethod(type, tmpCache);
            if (tmpCache.Count > 0)
            {
                foreach (var item in tmpCache)
                {
                    _cache.TryAdd(item.Key, item.Value);
                }
            }
            return func;
        });
        return (T)wrapperNew.Method(obj, new CacheDictionary(containsRepeatReference));

        #region GetCloneMethod
        Wrapper GetCloneMethod(Type type, Dictionary<Type, Wrapper> tmpCache)
        {
            if (_cache.TryGetValue(type, out var wrapper1)) return wrapper1;
            if (tmpCache.TryGetValue(type, out var wrapper2)) return wrapper2;
            var wrapper = new Wrapper();
            tmpCache.Add(type, wrapper);

            //基础类型
            var typecode = type.GetTypeCode();
            #region judgeSimple wrapper.Method = obj => obj;
            if (JudgeSimple(type))
            {
                wrapper.Method = (obj, dic) => obj;
                return wrapper;
            }
            #endregion

            var reflect = type.GetClassGenericFullName();
            if (type.IsArray)
            {
                GetCloneMethod_Array(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name == "Newtonsoft.Json.Linq.JObject")
            {
                var tmp = type.Assembly.GetType("Newtonsoft.Json.Linq.JObject");
                var tmp2 = tmp.GetMethod("DeepClone");
                var tmp3 = Expression.Parameter(typeof(object), "obj");
                var act = Expression.Lambda<Func<object, object>>(Expression.Call(getConvertExpr(tmp3, tmp), tmp2), tmp3).Compile();
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            else if (reflect.Name == "Newtonsoft.Json.Linq.JArray")
            {
                var tmp = type.Assembly.GetType("Newtonsoft.Json.Linq.JArray");
                var tmp2 = tmp.GetMethod("DeepClone");
                var tmp3 = Expression.Parameter(typeof(object), "obj");
                var act = Expression.Lambda<Func<object, object>>(Expression.Call(getConvertExpr(tmp3, tmp), tmp2), tmp3).Compile();
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            else if (reflect.Name == "Newtonsoft.Json.Linq.JToken")
            {
                var tmp = type.Assembly.GetType("Newtonsoft.Json.Linq.JToken");
                var tmp2 = tmp.GetMethod("DeepClone");
                var tmp3 = Expression.Parameter(typeof(object), "obj");
                var act = Expression.Lambda<Func<object, object>>(Expression.Call(getConvertExpr(tmp3, tmp), tmp2), tmp3).Compile();
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            #region TODO JsonObject/JsonArray/JsonDocument 原生没有提供clone方法
            else if (reflect.Name == "System.Text.Json.Nodes.JsonObject")
            {
                object act(object obj) => JsonObject.Parse((obj as JsonObject).ToJsonString());
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            else if (reflect.Name == "System.Text.Json.Nodes.JsonArray")
            {
                object act(object obj) => JsonArray.Parse((obj as JsonArray).ToJsonString());
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            else if (reflect.Name == "System.Text.Json.JsonDocument")
            {
                object act(object obj) => JsonDocument.Parse(JsonSerializer.Serialize(obj as JsonDocument));
                wrapper.Method = (obj, dic) => dic.TryGetValue(obj, out var val) ? val : act(obj);
                return wrapper;
            }
            #endregion
            else if (reflect.Name == "System.Collections.Generic.List<T>")
            {
                GetCloneMethod_List(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name == "System.Collections.Generic.Queue<T>")
            {
                GetCloneMethod_Queue(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name == "System.Collections.Generic.Stack<T>")
            {
                GetCloneMethod_Stack(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.ValueTuple<"))
            {
                GetCloneMethod_ValueTuple(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.Generic.Dictionary<TKey, TValue>"))
            {
                GetCloneMethod_Dictionary(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.Generic.HashSet<T>"))
            {
                GetCloneMethod_HashSet(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.Generic.SortedList<TKey, TValue>"))
            {
                GetCloneMethod_SortedList(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.Generic.SortedDictionary<TKey, TValue>"))
            {
                GetCloneMethod_SortedDictionary(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.Generic.LinkedList<T>"))
            {
                GetCloneMethod_LinkedList(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith(".<>f__AnonymousType"))
            {
                GetCloneMethod_Anonymous(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Tuple<"))
            {
                GetCloneMethod_Tuple(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (reflect.Name.StartsWith("System.Collections.ObjectModel.ReadOnlyCollection<T>"))
            {
                GetCloneMethod_ReadOnlyCollection(type, reflect, wrapper, tmpCache);
                return wrapper;
            }
            else if (type.GetInterface("System.ICloneable") != null)
            {
                //无论是值类型还是引用类型都可以实现ICloneable方法
                wrapper.Method = (obj, dic) => (obj as ICloneable).Clone();
                return wrapper;
            }
            else
            {
                var types = type.GetGenericArguments();

                var enumerable = type.GetInterfaces().Where(i => i.Name == "IEnumerable`1").FirstOrDefault();
                if (enumerable != null)
                {
                    reflect = enumerable.GetClassGenericFullName();
                    GetCloneMethod_IEnumerable(type, reflect, wrapper, tmpCache);
                }
                else if (type.IsValueType)
                {
                    GetCloneMethod_Struct(type, reflect, wrapper, tmpCache);
                }
                else
                {
                    GetCloneMethod_Poco(type, reflect, wrapper, tmpCache);
                }
                return wrapper;
            }
        }

        #region GetCommonExp
        (ParameterExpression para_obj,
                ParameterExpression para_dic,
                LabelTarget retLabel,
                LabelExpression retExp,
                ConditionalExpression ifnullExp,
                ConditionalExpression ifCacheExp
                ) GetCommonExp()
        {
            var para_obj = Expression.Parameter(typeof(object), "obj");//(object i)
            var para_dic = Expression.Parameter(typeof(CacheDictionary), "dic");//(dictionary<object,object> dic)
            var retLabel = Expression.Label(typeof(object), "ret");
            var retExp = Expression.Label(retLabel, para_obj);
            var ifnullExp = Expression.IfThen(Expression.Equal(para_obj, Expression.Constant(null)), Expression.Return(retLabel, para_obj));//if (i==null) return i;

            var ifCacheExp = Expression.IfThen(Expression.IsTrue(Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.ContainsKey)), para_obj)), Expression.Return(retLabel, Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Get)), para_obj)));//if (dic.ContainsKey(i)) return dic.Get(i);
            return (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp);
        }
        #endregion

        #region getConvertExpr
        UnaryExpression getConvertExpr(Expression expr, Type type)
        {
            if (!type.IsValueType && type.IsNullable()) return Expression.TypeAs(expr, type);
            return Expression.Convert(expr, type);
        }
        #endregion

        #region GetCloneMethod_List
        void GetCloneMethod_List(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//var res=int[];
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.Call(null, typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), getConvertExpr(para_obj, type)));//(obj as List<int>).ToList()
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, tmpRes);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, tmpAddDic, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //List<Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as List<Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //List<Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor([typeof(int)]), [countVar]);
            //res=new List<Person>(count);
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        Expression.Call(//res.Add(innerClone(obj2[i])
            localRes
                            , type.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , getConvertExpr(//innerClone(obj2[i]) as Person
                                Expression.Invoke(//innerClone(obj2[i])
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Convert(Expression.Call(localObj, type.GetMethod("get_Item"), [localVar_i]), typeof(object))//(object)obj2[i]
                                    , para_dic
            ),
                                reflect.GenericTypes[0].type)
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, countVar, localRes, localVar_i],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Queue
        void GetCloneMethod_Queue(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//Queue<int> res;
                var newExpr = Expression.New(type.GetConstructor([typeof(IEnumerable<>).MakeGenericType(reflect.GenericTypes[0].type)]), Expression.Call(null, typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), getConvertExpr(para_obj, type)));//new Queue<int>((obj as Queue<int>).ToList())
                var tmpAssignRes = Expression.Assign(tmpRes, newExpr);//(obj as List<int>).ToList()
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, tmpRes);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, tmpAddDic, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //List<Person>
            var midType = typeof(List<>).MakeGenericType(reflect.GenericTypes[0].type);
            //List<Person> obj2;
            var localObj = Expression.Parameter(midType, "obj2");
            //obj2=(obj as Queue<Person>).ToList();
            var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes[0].type);
            var assignLocalObj = Expression.Assign(localObj, Expression.Call(null, toListMethod, [getConvertExpr(para_obj, type)]));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //List<Person> midRes;
            var midRes = Expression.Variable(midType, "midRes");
            //Queue<Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(midType.GetConstructor([typeof(int)]), [countVar]);
            //res=new List<Person>(count);
            var assignRes = Expression.Assign(midRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        Expression.Call(//res.Add(innerClone(obj2[i])
            midRes
                            , midType.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , getConvertExpr(//innerClone(obj2[i]) as Person
                                Expression.Invoke(//innerClone(obj2[i])
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Convert(Expression.Call(localObj, midType.GetMethod("get_Item"), [localVar_i]), typeof(object))//(object)obj2[i]
                                    , para_dic
            ),
                                reflect.GenericTypes[0].type)
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var assignResFinal = Expression.Assign(localRes, Expression.New(type.GetConstructor([typeof(IEnumerable<>).MakeGenericType(reflect.GenericTypes[0].type)]), [midRes]));
            var block = Expression.Block(
                [localObj, countVar, midRes, localRes, localVar_i],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                assignRes,
                addDic,
                loopExp,
                assignResFinal,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Stack
        void GetCloneMethod_Stack(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            var reverseMethod = typeof(ListExtensions).GetMethods().First(i => i.Name == "ReverseFluent" && i.ReturnType.Name == "List`1").MakeGenericMethod(reflect.GenericTypes[0].type);
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//Stack<int> res;

                var newExpr = Expression.New(type.GetConstructor([typeof(IEnumerable<>).MakeGenericType(reflect.GenericTypes[0].type)]), Expression.Call(null, reverseMethod, [Expression.Call(null, typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), getConvertExpr(para_obj, type))]));//new Stack<int>((obj as Stack<int>).ToList().ReverseFluent())                
                var tmpAssignRes = Expression.Assign(tmpRes, newExpr);//res = new Stack<int>((obj as Stack<int>).ToList().ReverseFluent())
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, tmpRes);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, tmpAddDic, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //List<Person>
            var midType = typeof(List<>).MakeGenericType(reflect.GenericTypes[0].type);
            //List<Person> obj2;
            var localObj = Expression.Parameter(midType, "obj2");
            //obj2=(obj as Stack<Person>).ToList();
            var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes[0].type);
            var assignLocalObj = Expression.Assign(localObj, Expression.Call(null, toListMethod, [getConvertExpr(para_obj, type)]));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //List<Person> midRes;
            var midRes = Expression.Variable(midType, "midRes");
            //Stack<Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(midType.GetConstructor([typeof(int)]), [countVar]);
            //res=new List<Person>(count);
            var assignRes = Expression.Assign(midRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        Expression.Call(//res.Add(innerClone(obj2[i])
            midRes
                            , midType.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , getConvertExpr(//innerClone(obj2[i]) as Person
                                Expression.Invoke(//innerClone(obj2[i])
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Convert(Expression.Call(localObj, midType.GetMethod("get_Item"), [localVar_i]), typeof(object))//(object)obj2[i]
                                    , para_dic
            ),
                                reflect.GenericTypes[0].type)
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var assignResFinal = Expression.Assign(localRes, Expression.New(type.GetConstructor([typeof(IEnumerable<>).MakeGenericType(reflect.GenericTypes[0].type)]), [Expression.Call(null, reverseMethod, [midRes])]));
            var block = Expression.Block(
                [localObj, countVar, midRes, localRes, localVar_i],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                assignRes,
                addDic,
                loopExp,
                assignResFinal,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        bool judgeSimpleTuple((string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect)
        {
            var isSimple = true;
            for (var i = 0; i < reflect.GenericTypes.Count; i++)
            {
                var type = reflect.GenericTypes[i].type;
                var name = reflect.GenericTypes[i].name;
                if (name == "TRest")
                {
                    var newRTeflect = type.GetClassGenericFullName();
                    isSimple &= judgeSimpleTuple(newRTeflect);
                    if (!isSimple) return false;
                }
                else
                {
                    isSimple &= JudgeSimple(type);
                }
            }
            return isSimple;
        }

        #region GetCloneMethod_ValueTuple
        void GetCloneMethod_ValueTuple(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            if (judgeSimpleTuple(reflect))
            {
                wrapper.Method = (obj, dic) => obj;
                return;
            }
            var valueProp = Expression.Variable(type, "p");
            var para_obj = Expression.Parameter(typeof(object), "i");
            var para_dic = Expression.Parameter(typeof(CacheDictionary), "dic");
            var tmpExp = Expression.Convert(para_obj, type);

            var ctorVals = new List<Expression>();
            var ctor = type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());
            var circles = new List<(ConstructorInfo circleCtor, List<Expression> circleCtorVals)> { (ctor, ctorVals) };


            var args = new List<Expression>();
            for (int i = 0; i < reflect.GenericTypes.Count; i++)
            {
                var valueAccess = Expression.MakeMemberAccess(tmpExp, type.GetMember(i == 7 ? "Rest" : "Item" + (i + 1))[0]);
                var geneType = reflect.GenericTypes[i].type;
                if (JudgeSimple(geneType))
                {
                    args.Add(valueAccess);
                }
                else
                {
                    var cloneMethod = GetCloneMethod(geneType, tmpCache);
                    args.Add(Expression.Convert(Expression.Invoke(Expression.MakeMemberAccess(Expression.Constant(cloneMethod), typeof(Wrapper).GetMember("Method")[0]), Expression.Convert(valueAccess, typeof(object)), para_dic), geneType));
                }
            }

            var newExp = Expression.New(type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray()),
                [.. args]);
            var convertExp = Expression.Convert(newExp, typeof(object));
            var tmp = Expression.Lambda<Func<object, CacheDictionary, object>>(convertExp, para_obj, para_dic);
            wrapper.Method = tmp.Compile();
        }
        #endregion

        #region GetCloneMethod_Poco
        void GetCloneMethod_Poco(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //先获取所有构造函数
            var ctors = type.GetConstructors();
            //优先使用空参构造函数, 无论是否是 public
            var ctor = ctors.FirstOrDefault(i => i.GetParameters().Length == 0);
            ParameterInfo[] ctorParameters = null;
            if (ctor == null)
            {
                ctors = ctors.Where(i => i.IsPublic).ToArray();
                if (ctors.Length != 1)
                {
                    //只能有一个 否则失败
                    throw new NotSupportedException($"类: {type.GetClassFullName()} 没有空参构造函数, 也没有找到适合的public构造函数, 失败!");
                }
                ctor = ctors.First();
                ctorParameters = ctor.GetParameters();
            }

            var props = type.GetProperties().ToList();
            var publicFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).ToList();
            var propSets = new List<BinaryExpression>();

            var tmp = Expression.Variable(type, "tmp");//Person tmp
            var assign = Expression.Assign(tmp, getConvertExpr(para_obj, type));//tmp= obj as Person;
            var retVar = Expression.Variable(type, "res");//Person res;

            //先处理构造函数
            BinaryExpression assignRes;//构造函数赋值 res=new Person() or res=new Person(id,name)
            if (ctorParameters == null)
            {
                //使用空参构造函数
                assignRes = Expression.Assign(retVar, Expression.New(ctor));//res=new Person()
            }
            else
            {
                //使用带参构造函数
                var paras = new List<Expression>(ctorParameters.Length);
                for (int pidx = 0; pidx < ctorParameters.Length; pidx++)
                {
                    var item = ctorParameters[pidx];
                    MemberInfo member = (MemberInfo)props.FirstOrDefault(i => string.Equals(i.Name, item.Name, StringComparison.OrdinalIgnoreCase)) ?? publicFields.FirstOrDefault(i => string.Equals(i.Name, item.Name, StringComparison.OrdinalIgnoreCase));
                    if (member == null)
                    {
                        //没有找到 用参数类型的默认值
                        paras.Add(Expression.Constant(item.ParameterType.GetDefault(), item.ParameterType));
                        continue;
                    }
                    var memberType = (member is PropertyInfo propertyInfo) ? propertyInfo.PropertyType : ((FieldInfo)member).FieldType;
                    Expression getVal = null;
                    if (JudgeSimple(memberType))
                    {
                        getVal = ObjectExtensions.GetConvertExpr(Expression.MakeMemberAccess(tmp, member), item.ParameterType);
                    }
                    else
                    {
                        /*
                         * class Person
                         * {
                         *     ctor(Other other);
                         *     prop Other Other;
                         * }
                         * 
                         * class Other{}
                         */
                        //非简单类型, 判断类型是否可以赋值, 可以的话就再克隆赋值, 否则就报错
                        if (memberType.IsAssignableTo(item.ParameterType))
                        {
                            getVal = Expression.Convert(Expression.Invoke(Expression.MakeMemberAccess(Expression.Constant(GetCloneMethod(memberType, tmpCache)), typeof(Wrapper).GetMember("Method")[0]), Expression.Convert(Expression.MakeMemberAccess(tmp, member), typeof(object)), para_dic), item.ParameterType);
                        }
                        else
                        {
                            throw new NotSupportedException($"在克隆类({type.GetClassFullName()})时, 无法处理ctor({ctor})中第 {pidx + 1} 个参数, 因为无法从类型 {memberType.GetClassFullName()} 克隆赋值到 {item.ParameterType.GetClassFullName()}");
                        }
                    }
                    paras.Add(getVal);
                    if (member is PropertyInfo propertyInfo1) props.Remove(propertyInfo1);
                    else if (member is FieldInfo fieldInfo) publicFields.Remove(fieldInfo);
                }
                assignRes = Expression.Assign(retVar, Expression.New(ctor, paras));//res=new Person(id,name)
            }
            props = props.Where(i => i.CanWrite).ToList();

            //处理其他属性和public field
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, retVar);//dic.Add(i,res)

            for (int i = 0; i < props.Count; i++)
            {
                var prop = props[i];
                var flag = false;
                var propType = prop.PropertyType;
                if (JudgeSimple(propType))
                {
                    propSets.Add(Expression.Assign(Expression.Property(retVar, prop), Expression.MakeMemberAccess(tmp, prop)));
                    flag = true;
                }
                if (!flag)
                {
                    //其他的
                    propSets.Add(Expression.Assign(Expression.Property(retVar, prop), Expression.Convert(Expression.Invoke(Expression.MakeMemberAccess(Expression.Constant(GetCloneMethod(prop.PropertyType, tmpCache)), typeof(Wrapper).GetMember("Method")[0]), Expression.Convert(Expression.MakeMemberAccess(tmp, prop), typeof(object)), para_dic), propType)));
                    flag = true;
                }
            }
            for (int i = 0; i < publicFields.Count; i++)
            {
                var field = publicFields[i];
                var flag = false;
                var propType = field.FieldType;
                if (JudgeSimple(propType))
                {
                    propSets.Add(Expression.Assign(Expression.Field(retVar, field), Expression.MakeMemberAccess(tmp, field)));
                    flag = true;
                }
                if (!flag)
                {
                    //其他的
                    propSets.Add(Expression.Assign(Expression.Field(retVar, field), Expression.Convert(Expression.Invoke(Expression.MakeMemberAccess(Expression.Constant(GetCloneMethod(field.FieldType, tmpCache)), typeof(Wrapper).GetMember("Method")[0]), Expression.Convert(Expression.MakeMemberAccess(tmp, field), typeof(object)), para_dic), propType)));
                    flag = true;
                }
            }

            var exps = new List<Expression>() { ifnullExp, ifCacheExp, assign, assignRes, addDic };
            exps.AddRange(propSets);
            exps.Add(Expression.Goto(retLabel, retVar));
            exps.Add(retExp);

            var block = Expression.Block([tmp, retVar], [.. exps]);
            var memberInit = Expression.Lambda<Func<object, CacheDictionary, object>>(block, [para_obj, para_dic]);
            wrapper.Method = memberInit.Compile();
        }
        #endregion

        #region GetCloneMethod_Array
        void GetCloneMethod_Array(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();

            var eleType = type.GetElementType();
            if (JudgeSimple(eleType))
            {
                var tmpRes = Expression.Variable(type, "res");//var res=int[];
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.Call(null, typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(eleType), getConvertExpr(para_obj, type)));//res=(obj as int[]).ToArray()
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, tmpRes);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, tmpAddDic, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //Person[] obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as Person[];
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Length");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //Person[] res;
            var localRes = Expression.Variable(type, "res");

            //new Person[count]
            var newArrExp = Expression.NewArrayBounds(eleType, countVar);
            //var newListExp = Expression.New(type.GetConstructor(new Type[] { typeof(int) }), new Expression[] { countVar });
            //res=new Person[count];
            var assignRes = Expression.Assign(localRes, newArrExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);


            var innerClone = GetCloneMethod(eleType, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        Expression.Assign(Expression.ArrayAccess(localRes, localVar_i),
                               Expression.Convert(Expression.Invoke(//innerClone(obj2[i])
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.ArrayIndex(localObj, localVar_i), typeof(object))//(object)obj2[i]
                                    , para_dic
                                ), eleType)
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);

            var block = Expression.Block(
                [localObj, countVar, localRes, localVar_i],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
            );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Dictionary
        void GetCloneMethod_Dictionary(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//var res=Dictionary<int,string>;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.New(
                    type.GetConstructor([
                        typeof(IDictionary<,>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type, reflect.GenericTypes.LastOrDefault().type]),
                        typeof(IEqualityComparer<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]) ]
                    ),
                    getConvertExpr(para_obj, type),
                    Expression.Property(getConvertExpr(para_obj, type), "Comparer")));//res= new Dictionary<int,string>(obj as Dictionary<int,string>,(obj as Dictionary<int,string>).Comparer)
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //Dictionary<int,Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as Dictionary<int,Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //Dictionary<int,Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor([
                typeof(int),
                typeof(IEqualityComparer<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type) ]), [countVar, Expression.Property(localObj, "Comparer")]);
            //res=new Dictionary<int,Person>(obj2.Count,obj2.Comparer)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            //List<int> keys;
            var local_keys = Expression.Variable(typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type), "keys");
            //keys=obj2.Keys.ToList();
            var keysAssign = Expression.Assign(local_keys, Expression.Call(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), Expression.Property(localObj, "Keys")));


            Wrapper keyClone = null, valueClone = null;
            if (!JudgeSimple(reflect.GenericTypes.FirstOrDefault().type)) keyClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);
            if (!JudgeSimple(reflect.GenericTypes.LastOrDefault().type)) valueClone = GetCloneMethod(reflect.GenericTypes.LastOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //int key;
            var local_key = Expression.Variable(reflect.GenericTypes.FirstOrDefault().type, "key");
            var assignKey = Expression.Assign(local_key, Expression.Call(local_keys, typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type).GetMethod("get_Item"), [localVar_i]));
            var cloneKey = keyClone == null ? (local_key as Expression) :
                getConvertExpr(Expression.Invoke(//keyClone(key,dic)
                                    Expression.MakeMemberAccess(Expression.Constant(keyClone), typeof(Wrapper).GetMember("Method")[0])
                                    , local_key
                                    , para_dic), reflect.GenericTypes.FirstOrDefault().type);

            var cloneValue = valueClone == null ? (Expression.Call(localObj, type.GetMethod("get_Item"), local_key) as Expression) :
               getConvertExpr(Expression.Invoke(//valueClone(obj2[key],dic)
                                    Expression.MakeMemberAccess(Expression.Constant(valueClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Call(localObj, type.GetMethod("get_Item"), local_key)
                                    , para_dic), reflect.GenericTypes.LastOrDefault().type);

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        assignKey,
                        Expression.Call(//res.Add(key,value)
            localRes
                            , type.GetMethod("Add", [reflect.GenericTypes[0].type, reflect.GenericTypes[1].type])
                            , cloneKey
                            , cloneValue
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, countVar, localRes, localVar_i, local_keys, local_key],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                keysAssign,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_SortedList
        void GetCloneMethod_SortedList(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//var res=SortedList<int,string>;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.New(
                    type.GetConstructor([
                        typeof(SortedList<,>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type, reflect.GenericTypes.LastOrDefault().type]),
                        typeof(IComparer<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]) ]
                    ),
                    getConvertExpr(para_obj, type),
                    Expression.Property(getConvertExpr(para_obj, type), "Comparer")));//res= new SortedList<int,string>(obj as SortedList<int,string>,(obj as SortedList<int,string>).Comparer)
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //SortedList<int,Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as SortedList<int,Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //SortedList<int,Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor([
                typeof(int),
                typeof(IComparer<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type) ]), [countVar, Expression.Property(localObj, "Comparer")]);
            //res=new SortedList<int,Person>(obj2.Count,obj2.Comparer)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            //List<int> keys;
            var local_keys = Expression.Variable(typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type), "keys");
            //keys=obj2.Keys.ToList();
            var keysAssign = Expression.Assign(local_keys, Expression.Call(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), Expression.Property(localObj, "Keys")));


            Wrapper keyClone = null, valueClone = null;
            if (!JudgeSimple(reflect.GenericTypes.FirstOrDefault().type)) keyClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);
            if (!JudgeSimple(reflect.GenericTypes.LastOrDefault().type)) valueClone = GetCloneMethod(reflect.GenericTypes.LastOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //int key;
            var local_key = Expression.Variable(reflect.GenericTypes.FirstOrDefault().type, "key");
            var assignKey = Expression.Assign(local_key, Expression.Call(local_keys, typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type).GetMethod("get_Item"), [localVar_i]));
            var cloneKey = keyClone == null ? (local_key as Expression) :
                getConvertExpr(Expression.Invoke(//keyClone(key,dic)
                                    Expression.MakeMemberAccess(Expression.Constant(keyClone), typeof(Wrapper).GetMember("Method")[0])
                                    , local_key
                                    , para_dic), reflect.GenericTypes.FirstOrDefault().type);

            var cloneValue = valueClone == null ? (Expression.Call(localObj, type.GetMethod("get_Item"), local_key) as Expression) :
               getConvertExpr(Expression.Invoke(//valueClone(obj2[key],dic)
                                    Expression.MakeMemberAccess(Expression.Constant(valueClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Call(localObj, type.GetMethod("get_Item"), local_key)
                                    , para_dic), reflect.GenericTypes.LastOrDefault().type);

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        assignKey,
                        Expression.Call(//res.Add(key,value)
            localRes
                            , type.GetMethod("Add", [reflect.GenericTypes[0].type, reflect.GenericTypes[1].type])
                            , cloneKey
                            , cloneValue
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, countVar, localRes, localVar_i, local_keys, local_key],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                keysAssign,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_SortedDictionary
        void GetCloneMethod_SortedDictionary(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//var res=SortedDictionary<int,string>;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.New(
                    type.GetConstructor([
                        typeof(SortedDictionary<,>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type, reflect.GenericTypes.LastOrDefault().type]),
                        typeof(IComparer<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]) ]
                    ),
                    getConvertExpr(para_obj, type),
                    Expression.Property(getConvertExpr(para_obj, type), "Comparer")));//res= new SortedDictionary<int,string>(obj as SortedDictionary<int,string>,(obj as SortedDictionary<int,string>).Comparer)
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //SortedDictionary<int,Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as SortedDictionary<int,Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));
            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            //SortedDictionary<int,Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor([
                typeof(IComparer<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type) ]), [Expression.Property(localObj, "Comparer")]);
            //res=new SortedDictionary<int,Person>(obj2.Count,obj2.Comparer)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            //List<int> keys;
            var local_keys = Expression.Variable(typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type), "keys");
            //keys=obj2.Keys.ToList();
            var keysAssign = Expression.Assign(local_keys, Expression.Call(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(reflect.GenericTypes.FirstOrDefault().type), Expression.Property(localObj, "Keys")));


            Wrapper keyClone = null, valueClone = null;
            if (!JudgeSimple(reflect.GenericTypes.FirstOrDefault().type)) keyClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);
            if (!JudgeSimple(reflect.GenericTypes.LastOrDefault().type)) valueClone = GetCloneMethod(reflect.GenericTypes.LastOrDefault().type, tmpCache);

            //for(var i=0;i<count;i++)
            //int i;
            var localVar_i = Expression.Variable(typeof(int), "i");
            //i=0;
            var loopInit = Expression.Assign(localVar_i, Expression.Constant(0));//i=0
            var breakLabel = Expression.Label("break");

            //int key;
            var local_key = Expression.Variable(reflect.GenericTypes.FirstOrDefault().type, "key");
            var assignKey = Expression.Assign(local_key, Expression.Call(local_keys, typeof(List<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type).GetMethod("get_Item"), [localVar_i]));
            var cloneKey = keyClone == null ? (local_key as Expression) :
                getConvertExpr(Expression.Invoke(//keyClone(key,dic)
                                    Expression.MakeMemberAccess(Expression.Constant(keyClone), typeof(Wrapper).GetMember("Method")[0])
                                    , local_key
                                    , para_dic), reflect.GenericTypes.FirstOrDefault().type);

            var cloneValue = valueClone == null ? (Expression.Call(localObj, type.GetMethod("get_Item"), local_key) as Expression) :
               getConvertExpr(Expression.Invoke(//valueClone(obj2[key],dic)
                                    Expression.MakeMemberAccess(Expression.Constant(valueClone), typeof(Wrapper).GetMember("Method")[0])
                                    , Expression.Call(localObj, type.GetMethod("get_Item"), local_key)
                                    , para_dic), reflect.GenericTypes.LastOrDefault().type);

            //res.Add(innerClone(obj2[i]))
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.LessThan(localVar_i, countVar),//if(i<count)
                    Expression.Block(
                        assignKey,
                        Expression.Call(//res.Add(key,value)
            localRes
                            , type.GetMethod("Add", [reflect.GenericTypes[0].type, reflect.GenericTypes[1].type])
                            , cloneKey
                            , cloneValue
                        ),
                         Expression.PostIncrementAssign(localVar_i)//i++;
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, countVar, localRes, localVar_i, local_keys, local_key],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                keysAssign,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_HashSet
        void GetCloneMethod_HashSet(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//HaseSet<int> res;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.New(
                    type.GetConstructor([
                        typeof(HashSet<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]),
                        typeof(IEqualityComparer<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]) ]
                    ),
                    getConvertExpr(para_obj, type),
                    Expression.Property(getConvertExpr(para_obj, type), "Comparer")));//res= new HaseSet<int>(obj as HaseSet<int>,(obj as HaseSet<int>).Comparer)
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //HaseSet<Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as HaseSet<Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));

            //int count;
            var countVar = Expression.Variable(typeof(int), "count");
            var countProp = Expression.Property(localObj, "Count");
            //count=obj2.Count;
            var assignCount = Expression.Assign(countVar, countProp);

            var ratorType = typeof(HashSet<>.Enumerator).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type);
            //HashSet<Person>.Enumerator rator;
            var ratorVar = Expression.Variable(ratorType, "rator");
            //rator=obj2.GetEnumerator();
            var assignRator = Expression.Assign(ratorVar, Expression.Call(localObj, type.GetMethod("GetEnumerator")));

            //HashSet<Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor([
                typeof(int),
                typeof(IEqualityComparer<>).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type) ]), [countVar, Expression.Property(localObj, "Comparer")]);
            //res=new HashSet<Person>(obj2.Count,obj2.Comparer)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var breakLabel = Expression.Label("break");

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //while (rator.MoveNext())
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.IsTrue(Expression.Call(ratorVar, ratorType.GetMethod("MoveNext"))),
                    Expression.Block(
                        Expression.Call(//res.Add(innerClone(rator.Current))
            localRes
                            , type.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , Expression.Convert(Expression.Invoke(//innerClone(rator.Current)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(ratorVar, "Current"), typeof(object))//(object)rator.Current
                                    , para_dic
                                ), reflect.GenericTypes.FirstOrDefault().type)
                        )
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, countVar, localRes, ratorVar],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignCount,
                assignRator,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_LinkedList
        void GetCloneMethod_LinkedList(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                var tmpRes = Expression.Variable(type, "res");//LinkedList<int> res;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.New(
                    type.GetConstructor([
                        typeof(IEnumerable<>).MakeGenericType([reflect.GenericTypes.FirstOrDefault().type]) ]
                    ),
                    getConvertExpr(para_obj, type)));//res= new LinkedList<int>(obj as LinkedList<int>)
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //LinkedList<Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as LinkedList<Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));

            var ratorType = typeof(LinkedList<>.Enumerator).MakeGenericType(reflect.GenericTypes.FirstOrDefault().type);
            //LinkedList<Person>.Enumerator rator;
            var ratorVar = Expression.Variable(ratorType, "rator");
            //rator=obj2.GetEnumerator();
            var assignRator = Expression.Assign(ratorVar, Expression.Call(localObj, type.GetMethod("GetEnumerator")));

            //LinkedList<Person> res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(type.GetConstructor(Array.Empty<Type>()));
            //res=new LinkedList<Person>()
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var breakLabel = Expression.Label("break");

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //while (rator.MoveNext())
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.IsTrue(Expression.Call(ratorVar, ratorType.GetMethod("MoveNext"))),
                    Expression.Block(
                        Expression.Call(//res.AddLast(innerClone(rator.Current))
            localRes
                            , type.GetMethod("AddLast", [reflect.GenericTypes[0].type])
                            , Expression.Convert(Expression.Invoke(//innerClone(rator.Current)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(ratorVar, "Current"), typeof(object))//(object)rator.Current
                                    , para_dic
                                ), reflect.GenericTypes.FirstOrDefault().type)
                        )
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, localRes, ratorVar],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignRator,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Anonymous
        void GetCloneMethod_Anonymous(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();
            //simple
            if (JudgeSimple(reflect.GenericTypes.FirstOrDefault().type) && JudgeSimple(reflect.GenericTypes.LastOrDefault().type))
            {
                wrapper.Method = (para_obj, para_dic) => para_obj;
                return;
            }

            //{Id,Name} obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as {Id,Name};
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));

            var ctor = type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());
            var paras = new List<Expression>();
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < reflect.GenericTypes.Count; i++)
            {
                var propType = reflect.GenericTypes[i].type;
                if (JudgeSimple(propType)) paras.Add(Expression.Property(localObj, props[i].Name));
                else
                {
                    var innerClone = GetCloneMethod(propType, tmpCache);
                    paras.Add(Expression.Convert(Expression.Invoke(//innerClone(obj2.Person)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(localObj, props[i].Name), typeof(object)) //(object)obj2.Person
                                    , para_dic
                                ), propType));
                }
            }

            //{Id,Name,Person} res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(ctor, [.. paras]);
            //res=new Anonymous(obj2.Id,obj2.Name,innerClone(obj2.Person)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);


            var block = Expression.Block(
                [localObj, localRes],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignRes,
                addDic,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Tuple
        void GetCloneMethod_Tuple(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();

            //Tuple<int,Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as Tuple<int,Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));

            var ctor = type.GetConstructor(reflect.GenericTypes.Select(i => i.type).ToArray());
            var paras = new List<Expression>();
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < reflect.GenericTypes.Count; i++)
            {
                var propType = reflect.GenericTypes[i].type;
                if (JudgeSimple(propType)) paras.Add(Expression.Property(localObj, props[i].Name));
                else
                {
                    var innerClone = GetCloneMethod(propType, tmpCache);
                    paras.Add(Expression.Convert(Expression.Invoke(//innerClone(obj2.Person)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(localObj, props[i].Name), typeof(object))//(object)obj2.Person
                                    , para_dic
                                ), propType));
                }
            }

            //{Id,Name,Person} res;
            var localRes = Expression.Variable(type, "res");
            var newListExp = Expression.New(ctor, [.. paras]);
            //res=new Anonymous(obj2.Id,obj2.Name,innerClone(obj2.Person)
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);


            var block = Expression.Block(
                [localObj, localRes],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignRes,
                addDic,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_IEnumerable
        void GetCloneMethod_IEnumerable(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();

            var eleType = reflect.GenericTypes.FirstOrDefault().type;
            var finalType = typeof(List<>).MakeGenericType(eleType);
            var ienumerableType = typeof(IEnumerable<>).MakeGenericType(eleType);
            if (JudgeSimple(eleType))
            {
                var tmpRes = Expression.Variable(finalType, "res");//List<int> res;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.Call(null, typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(eleType), getConvertExpr(para_obj, type)));//res=(obj as IEnumerable<int>).List()
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, tmpRes);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes], ifnullExp, ifCacheExp, tmpAssignRes, tmpAddDic, Expression.Goto(retLabel, tmpRes), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //IEnumerable<Person> obj2;
            var localObj = Expression.Parameter(ienumerableType, "obj2");
            //obj2=obj as IEnumerable<Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, ienumerableType));

            var ratorType = typeof(IEnumerator);
            //IEnumerator<Person> rator;
            var ratorVar = Expression.Variable(ratorType, "rator");
            //rator=obj2.GetEnumerator();
            var assignRator = Expression.Assign(ratorVar, Expression.Call(localObj, typeof(IEnumerable<>).MakeGenericType(eleType).GetMethod("GetEnumerator")));

            //List<Person> res;
            var localRes = Expression.Variable(finalType, "res");
            var newListExp = Expression.New(finalType.GetConstructor(Array.Empty<Type>()));
            //res=new List<Person>()
            var assignRes = Expression.Assign(localRes, newListExp);
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var breakLabel = Expression.Label("break");

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //while (rator.MoveNext())
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.IsTrue(Expression.Call(ratorVar, ratorType.GetMethod("MoveNext"))),
                    Expression.Block(
                        Expression.Call(//res.AddLast(innerClone(rator.Current))
            localRes
                            , finalType.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , Expression.Convert(Expression.Invoke(//innerClone(rator.Current)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(ratorVar, "Current"), typeof(object))//(object)rator.Current
                                    , para_dic
                                ), reflect.GenericTypes.FirstOrDefault().type)
                        )
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, localRes, ratorVar],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignRator,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_ReadOnlyCollection
        void GetCloneMethod_ReadOnlyCollection(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();

            var eleType = reflect.GenericTypes.FirstOrDefault().type;
            if (JudgeSimple(eleType))
            {
                var tmpRes = Expression.Variable(typeof(List<>).MakeGenericType(eleType), "tmpRes");//List<int> res;
                var tmpAssignRes = Expression.Assign(tmpRes, Expression.Call(null, typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(eleType), getConvertExpr(para_obj, type)));//res=(obj as ReadOnlyCollection<int>).List()
                var tmpReadonly = Expression.New(type.GetConstructor(new[] { typeof(IList<>).MakeGenericType(eleType) }), tmpRes);
                var res = Expression.Variable(type, "res");//ReadOnlyCollection<int> res;
                var finalAssign = Expression.Assign(res, tmpReadonly);
                var tmpAddDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, res);//dic.Add(i,res);
                var tmpBlock = Expression.Block([tmpRes, res], ifnullExp, ifCacheExp, tmpAssignRes, finalAssign, tmpAddDic, Expression.Goto(retLabel, res), retExp);
                var lambda = Expression.Lambda<Func<object, CacheDictionary, object>>(tmpBlock, para_obj, para_dic);
                wrapper.Method = lambda.Compile();
                return;
            }

            //var ienumerableType = typeof(IEnumerable<>).MakeGenericType(eleType);
            var listType = typeof(List<>).MakeGenericType(eleType);
            //ReadOnlyCollection<Person> obj2;
            var localObj = Expression.Parameter(type, "obj2");
            //obj2=obj as IEnumerable<Person>;
            var assignLocalObj = Expression.Assign(localObj, getConvertExpr(para_obj, type));

            var ratorType = typeof(IEnumerator);
            //IEnumerator<Person> rator;
            var ratorVar = Expression.Variable(ratorType, "rator");
            //rator=obj2.GetEnumerator();
            var assignRator = Expression.Assign(ratorVar, Expression.Call(localObj, typeof(IEnumerable<>).MakeGenericType(eleType).GetMethod("GetEnumerator")));

            //List<Person> tmpRes;
            var localTmpRes = Expression.Variable(listType, "tmpRes");
            var newListExp = Expression.New(listType.GetConstructor([typeof(int)]), Expression.Property(localObj, "Count"));
            //res=new List<Person>(obj.Count)
            var assignTmpRes = Expression.Assign(localTmpRes, newListExp);
            var localRes = Expression.Variable(type, "res");
            var assignRes = Expression.Assign(localRes, Expression.New(type.GetConstructor([typeof(IList<>).MakeGenericType(eleType)]), localTmpRes));
            var addDic = Expression.Call(para_dic, typeof(CacheDictionary).GetMethod(nameof(CacheDictionary.Set)), para_obj, localRes);

            var breakLabel = Expression.Label("break");

            var innerClone = GetCloneMethod(reflect.GenericTypes.FirstOrDefault().type, tmpCache);

            //while (rator.MoveNext())
            var loopBody = Expression.Block(
                Expression.IfThenElse(Expression.IsTrue(Expression.Call(ratorVar, ratorType.GetMethod("MoveNext"))),
                    Expression.Block(
                        Expression.Call(//tmpRes.Add(innerClone(rator.Current))
            localTmpRes
                            , listType.GetMethod("Add", [reflect.GenericTypes[0].type])
                            , Expression.Convert(Expression.Invoke(//innerClone(rator.Current)
                                    Expression.MakeMemberAccess(Expression.Constant(innerClone), typeof(Wrapper).GetMember("Method")[0])//wrapper.Method.Invoke()
                                    , Expression.Convert(Expression.Property(ratorVar, "Current"), typeof(object)) //(object)rator.Current
                                    , para_dic
                                ), reflect.GenericTypes.FirstOrDefault().type)
                        )
                    )
                    , Expression.Goto(breakLabel))
            );
            var loopExp = Expression.Loop(loopBody, breakLabel);
            var block = Expression.Block(
                [localObj, localRes, ratorVar, localTmpRes],
                ifnullExp,
                ifCacheExp,
                assignLocalObj,
                assignRator,
                assignTmpRes,
                assignRes,
                addDic,
                loopExp,
                Expression.Goto(retLabel, localRes),
                retExp
                );
            var finalExp = Expression.Lambda<Func<object, CacheDictionary, object>>(block, para_obj, para_dic);
            wrapper.Method = finalExp.Compile();
        }
        #endregion

        #region GetCloneMethod_Struct
        void GetCloneMethod_Struct(Type type, (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) reflect, Wrapper wrapper, Dictionary<Type, Wrapper> tmpCache)
        {
            var (para_obj, para_dic, retLabel, retExp, ifnullExp, ifCacheExp) = GetCommonExp();

            var props = type.GetProperties().Where(i => i.CanWrite).ToList();
            if (props.All(i => JudgeSimple(i.PropertyType)))
            {
                wrapper.Method = (obj, cache) => obj;
                return;
            }
            var res = Expression.Variable(type, "res");//MyStruct res;
            var assignRes = Expression.Assign(res, Expression.Convert(para_obj, type));
            var propSets = new List<BinaryExpression>();
            for (int i = 0; i < props.Count; i++)
            {
                var prop = props[i];
                var flag = false;
                var propType = prop.PropertyType;
                if (JudgeSimple(propType))
                {
                    flag = true;
                }
                if (!flag)
                {
                    //其他的
                    propSets.Add(Expression.Assign(Expression.Property(res, prop), Expression.Convert(Expression.Invoke(Expression.MakeMemberAccess(Expression.Constant(GetCloneMethod(prop.PropertyType, tmpCache)), typeof(Wrapper).GetMember("Method")[0]), Expression.Convert(Expression.MakeMemberAccess(res, prop), typeof(object)), para_dic), propType)));
                    flag = true;
                }
            }

            var exps = new List<Expression>() { assignRes };
            exps.AddRange(propSets);
            exps.Add(Expression.Convert(res, typeof(object)));

            var block = Expression.Block([res], [.. exps]);
            var memberInit = Expression.Lambda<Func<object, CacheDictionary, object>>(block, [para_obj, para_dic]);
            wrapper.Method = memberInit.Compile();
        }
        #endregion
        #endregion

    }

    /// <summary>
    /// 注册指定 Type 的克隆逻辑, 如:
    /// <code>
    /// DeepCloneHelper.RegisterCloneHander(typeof(MyClass), (obj, cache) =>
    /// {
    ///     if (cache.ContainsKey(obj)) return cache.Get(obj);
    ///     var res = new MyClass((obj as MyClass).Id);
    ///     cache.Set(obj, res);
    ///     return res;
    /// });
    /// var cls = new MyClass(1);
    /// var newCls = cls.DeepClone();
    /// Assert.That(cls != newCls);
    /// newCls.Id.ShouldBe(1);
    /// </code>
    /// </summary>
    public static void RegisterCloneHander(Type type, Func<object, CacheDictionary, object> func)
    {
        AssertUtil.NotNull(type);
        AssertUtil.NotNull(func);
        _cache.TryAdd(type, new Wrapper { Method = func });
    }
}
